/*
 * clientLogger.c
 * the implementation of some logging client functions such as opening the FIFO, and write to FIFO
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <sys/time.h>
#include <assert.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdarg.h>
#include <fcntl.h>
#include <stdbool.h>
#include <string.h>
#include <pthread.h>
#include "clientLogger.h"
#include "fileLockHelper.h"

static const char * FIFO_NAME = "/var/log/mySyslogd.FIFO";
static pthread_mutex_t loggingMutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;

/* the file descriptor for FIFO */
static int fifoFD = -1;

/* the output buffer for the time str in the log*/
static char timeStrBuffer[50];

/* the output buffer that we'll use for logging the message */
static char printBuffer[1024];
static char printBuffer2[1224];

static bool isFifoOpened() {
	return (-1 != fifoFD);
}

static void openFifoForWriting() {
	/* Don't open the FIFO twice. */
	if (-1 != fifoFD) {
		fprintf(stderr, "FIFO is already opened");
		return;
	}

	/*	open the FIFO for writing */
	fifoFD = open(FIFO_NAME, O_WRONLY);
	if (-1 == fifoFD) {
		fprintf(stderr,
				"Failed to open FIFO \"%s\". Program will terminate!!!\n",
				FIFO_NAME);
		perror("fifo error");
		exit(-1);
	}

}

static char * getCurrentTimeStr() {

	char buffer[11];
	time_t t;
	time(&t);
	strftime(buffer, sizeof(buffer), "%X", localtime(&t));
	struct timeval tv;
	gettimeofday(&tv, 0);
	sprintf(timeStrBuffer, "%s.%03ld", buffer, (long) tv.tv_usec / 1000);

	return timeStrBuffer;
}


void logDebug(const char * module, const char * msg) {

	assert(NULL != module);
	assert(NULL != msg);

	// lock the mutex so that this thread can access the print buffers and FIFOs atomically
	pthread_mutex_lock(&loggingMutex);

	if (!isFifoOpened()) {
		openFifoForWriting();
	}

	memset(printBuffer2, 0, sizeof(printBuffer2));

	snprintf(printBuffer2, sizeof(printBuffer2) - 1, "%s | PID-%ld | %s | %s\n",
			getCurrentTimeStr(), (long) getpid(), module, msg);

//	fprintf(stderr, "writing to FIFO: %s\n", printBuffer2);

	/* set a writer lock to the FIFO, so that the writing doesn't
	 * get messed up with other processes sharing this FIFO
	 */

	setFileLock(fifoFD, F_WRLCK, true);

	write(fifoFD, printBuffer2, strlen(printBuffer2));

	/* release the writer lock*/
	releaseFileLock(fifoFD);

	// unlokc the mutex
	pthread_mutex_unlock(&loggingMutex);
}

void logDebugVarg(const char * module, char * format, ...)
{
	assert(NULL != module);
	assert(NULL != format);

	// lock the mutex so that this thread can access the print buffers and FIFOs atomically
	pthread_mutex_lock(&loggingMutex);

	va_list args;
	va_start (args, format);

	memset(printBuffer, 0, sizeof(printBuffer));

	vsnprintf (printBuffer, sizeof(printBuffer)-1, format, args);

	va_end (args);

	logDebug(module, printBuffer);

	// unlokc the mutex
	pthread_mutex_unlock(&loggingMutex);

}


/* do some initialization before logging starts */
void initLogging() {
	if (!isFifoOpened()) {
		openFifoForWriting();
	}
}

/* do some clean ups before existing */
void closeLogging() {
	close(fifoFD);
}
